package com.harmony.pilot.completion.service.impl;

import com.harmony.pilot.common.dto.ASTSymbol;
import com.harmony.pilot.common.dto.CodeContext;
import com.harmony.pilot.common.dto.Location;
import com.harmony.pilot.completion.service.NavigationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 代码导航服务实现
 * 使用CodeQL提供代码导航功能
 */
@Service
@Slf4j
public class NavigationServiceImpl implements NavigationService {
    
    private final WebClient dataServiceWebClient;
    
    public NavigationServiceImpl(@Value("${service.data.url}") String dataServiceUrl) {
        this.dataServiceWebClient = WebClient.builder()
                .baseUrl(dataServiceUrl)
                .build();
    }
    
    @Override
    @Cacheable(value = "definitions", key = "#symbol + ':' + #context.filePath")
    public Location goToDefinition(String symbol, CodeContext context) {
        log.info("Finding definition for symbol: {} in {}", symbol, context.getFilePath());
        
        try {
            // 使用CodeQL查询符号定义
            List<ASTSymbol> symbols = querySymbolDefinition(symbol, context);
            
            if (symbols.isEmpty()) {
                log.warn("Symbol definition not found: {}", symbol);
                return null;
            }
            
            // 返回第一个匹配的定义（通常是最相关的）
            ASTSymbol definition = symbols.get(0);
            Location location = new Location();
            location.setFilePath(definition.getFilePath());
            location.setLine(definition.getStartLine() > 0 ? definition.getStartLine() : definition.getLine());
            location.setColumn(definition.getStartColumn() > 0 ? definition.getStartColumn() : definition.getColumn());
            
            return location;
            
        } catch (Exception e) {
            log.error("Failed to find definition", e);
            return null;
        }
    }
    
    @Override
    @Cacheable(value = "references", key = "#symbol + ':' + #context.filePath")
    public List<Location> findReferences(String symbol, CodeContext context) {
        log.info("Finding references for symbol: {}", symbol);
        
        List<Location> references = new ArrayList<>();
        
        try {
            // 使用CodeQL查询符号的所有引用
            List<ASTSymbol> refs = querySymbolReferences(symbol, context);
            
            for (ASTSymbol ref : refs) {
                Location location = new Location();
                location.setFilePath(ref.getFilePath());
                location.setLine(ref.getStartLine() > 0 ? ref.getStartLine() : ref.getLine());
                location.setColumn(ref.getStartColumn() > 0 ? ref.getStartColumn() : ref.getColumn());
                references.add(location);
            }
            
            log.info("Found {} references for symbol: {}", references.size(), symbol);
            
        } catch (Exception e) {
            log.error("Failed to find references", e);
        }
        
        return references;
    }
    
    @Override
    public List<Location> findImplementations(String symbol, CodeContext context) {
        log.info("Finding implementations for symbol: {}", symbol);
        
        // 对于接口或抽象类，查找实现
        // 使用CodeQL查询继承关系
        List<Location> implementations = new ArrayList<>();
        
        try {
            // TODO: 实现查找接口/抽象类的实现
            // 使用CodeQL查询继承关系
            
        } catch (Exception e) {
            log.error("Failed to find implementations", e);
        }
        
        return implementations;
    }
    
    @Override
    @Cacheable(value = "symbol-search", key = "#query + ':' + #projectRoot")
    public List<SymbolInfo> searchSymbols(String query, String projectRoot, int maxResults) {
        log.info("Searching symbols: {}", query);
        
        List<SymbolInfo> symbols = new ArrayList<>();
        
        try {
            // 使用CodeQL搜索符号
            CodeContext context = new CodeContext();
            context.setProjectRoot(projectRoot);
            
            // 查询匹配的符号
            List<ASTSymbol> astSymbols = querySymbolsByPattern(query, projectRoot);
            
            for (ASTSymbol astSymbol : astSymbols) {
                if (symbols.size() >= maxResults) {
                    break;
                }
                
                SymbolInfo info = new SymbolInfo();
                info.setName(astSymbol.getName());
                info.setQualifiedName(astSymbol.getQualifiedName());
                info.setKind(astSymbol.getType());
                info.setFilePath(astSymbol.getFilePath());
                
                Location location = new Location();
                location.setFilePath(astSymbol.getFilePath());
                location.setLine(astSymbol.getStartLine() > 0 ? astSymbol.getStartLine() : astSymbol.getLine());
                location.setColumn(astSymbol.getStartColumn() > 0 ? astSymbol.getStartColumn() : astSymbol.getColumn());
                info.setLocation(location);
                
                symbols.add(info);
            }
            
            log.info("Found {} symbols matching: {}", symbols.size(), query);
            
        } catch (Exception e) {
            log.error("Failed to search symbols", e);
        }
        
        return symbols;
    }
    
    /**
     * 查询符号定义
     */
    private List<ASTSymbol> querySymbolDefinition(String symbol, CodeContext context) {
        // 使用CodeQL查询符号定义
        try {
            // 优先使用find-definition查询
            Map<String, Object> request = new HashMap<>();
            request.put("queryFile", "navigation/find-definition");
            request.put("format", "json");
            
            Map<String, Object> parameters = new HashMap<>();
            parameters.put("symbolName", symbol);
            parameters.put("qualifiedName", "");  // 可以从context中提取
            parameters.put("filePath", context.getFilePath() != null ? context.getFilePath() : "");
            request.put("parameters", parameters);
            
            @SuppressWarnings("unchecked")
            Map<String, Object> response = dataServiceWebClient.post()
                    .uri("/data/codeql/query")
                    .bodyValue(request)
                    .retrieve()
                    .bodyToMono(Map.class)
                    .block();
            
            if (response != null && "200".equals(String.valueOf(response.get("code")))) {
                @SuppressWarnings("unchecked")
                Map<String, Object> data = (Map<String, Object>) response.get("data");
                if (data != null && data.get("jsonResults") != null) {
                    @SuppressWarnings("unchecked")
                    List<Map<String, Object>> results = (List<Map<String, Object>>) data.get("jsonResults");
                    
                    List<ASTSymbol> symbols = new ArrayList<>();
                    for (Map<String, Object> result : results) {
                        ASTSymbol astSymbol = parseDefinitionResult(result);
                        if (astSymbol != null) {
                            symbols.add(astSymbol);
                        }
                    }
                    return symbols;
                }
            }
            
            // 如果CodeQL查询失败，回退到AST符号查询
            return querySymbolDefinitionFallback(symbol, context);
        } catch (Exception e) {
            log.error("Failed to query symbol definition", e);
            // 回退到AST符号查询
            return querySymbolDefinitionFallback(symbol, context);
        }
    }
    
    /**
     * 回退方法：使用AST符号查询
     */
    private List<ASTSymbol> querySymbolDefinitionFallback(String symbol, CodeContext context) {
        try {
            Map<String, Object> request = new HashMap<>();
            request.put("symbol", symbol);
            request.put("maxResults", 10);
            
            Map<String, Object> queryContext = new HashMap<>();
            queryContext.put("filePath", context.getFilePath());
            queryContext.put("code", context.getCode());
            request.put("context", queryContext);
            
            @SuppressWarnings("unchecked")
            Map<String, Object> response = dataServiceWebClient.post()
                    .uri("/data/codeql/symbols/ast")
                    .bodyValue(request)
                    .retrieve()
                    .bodyToMono(Map.class)
                    .block();
            
            if (response != null && "200".equals(String.valueOf(response.get("code")))) {
                @SuppressWarnings("unchecked")
                List<Map<String, Object>> symbolsData = (List<Map<String, Object>>) response.get("data");
                List<ASTSymbol> symbols = new ArrayList<>();
                
                if (symbolsData != null) {
                    for (Map<String, Object> symbolData : symbolsData) {
                        ASTSymbol astSymbol = convertToASTSymbol(symbolData);
                        if (astSymbol != null) {
                            symbols.add(astSymbol);
                        }
                    }
                }
                
                return symbols;
            }
        } catch (Exception e) {
            log.error("Failed to query symbol definition", e);
        }
        return new ArrayList<>();
    }
    
    /**
     * 转换为ASTSymbol
     */
    private ASTSymbol convertToASTSymbol(Map<String, Object> data) {
        ASTSymbol symbol = new ASTSymbol();
        symbol.setName((String) data.get("name"));
        symbol.setQualifiedName((String) data.get("qualifiedName"));
        symbol.setType((String) data.get("type"));
        symbol.setFilePath((String) data.get("filePath"));
        if (data.get("startLine") != null) {
            symbol.setStartLine(((Number) data.get("startLine")).intValue());
        }
        if (data.get("startColumn") != null) {
            symbol.setStartColumn(((Number) data.get("startColumn")).intValue());
        }
        return symbol;
    }
    
    /**
     * 解析定义查询结果
     */
    private ASTSymbol parseDefinitionResult(Map<String, Object> result) {
        ASTSymbol symbol = new ASTSymbol();
        
        if (result.get("name") != null) {
            symbol.setName(result.get("name").toString());
        }
        if (result.get("qualifiedName") != null) {
            symbol.setQualifiedName(result.get("qualifiedName").toString());
        }
        if (result.get("filePath") != null) {
            symbol.setFilePath(result.get("filePath").toString());
        }
        if (result.get("line") != null) {
            symbol.setStartLine(((Number) result.get("line")).intValue());
            symbol.setLine(symbol.getStartLine());
        }
        if (result.get("column") != null) {
            symbol.setStartColumn(((Number) result.get("column")).intValue());
            symbol.setColumn(symbol.getStartColumn());
        }
        symbol.setType("Function");  // 默认类型
        
        return symbol;
    }
    
    /**
     * 查询符号引用
     */
    private List<ASTSymbol> querySymbolReferences(String symbol, CodeContext context) {
        // 使用CodeQL查询所有引用
        try {
            // 使用find-references查询
            Map<String, Object> request = new HashMap<>();
            request.put("queryFile", "navigation/find-references");
            request.put("format", "json");
            
            Map<String, Object> parameters = new HashMap<>();
            parameters.put("symbolName", symbol);
            request.put("parameters", parameters);
            
            @SuppressWarnings("unchecked")
            Map<String, Object> response = dataServiceWebClient.post()
                    .uri("/data/codeql/query")
                    .bodyValue(request)
                    .retrieve()
                    .bodyToMono(Map.class)
                    .block();
            
            if (response != null && "200".equals(String.valueOf(response.get("code")))) {
                @SuppressWarnings("unchecked")
                Map<String, Object> data = (Map<String, Object>) response.get("data");
                if (data != null && data.get("jsonResults") != null) {
                    @SuppressWarnings("unchecked")
                    List<Map<String, Object>> results = (List<Map<String, Object>>) data.get("jsonResults");
                    
                    List<ASTSymbol> symbols = new ArrayList<>();
                    for (Map<String, Object> result : results) {
                        ASTSymbol astSymbol = parseReferenceResult(result);
                        if (astSymbol != null) {
                            symbols.add(astSymbol);
                        }
                    }
                    return symbols;
                }
            }
        } catch (Exception e) {
            log.error("Failed to query symbol references", e);
        }
        return new ArrayList<>();
    }
    
    /**
     * 解析引用查询结果
     */
    private ASTSymbol parseReferenceResult(Map<String, Object> result) {
        ASTSymbol symbol = new ASTSymbol();
        
        // CodeQL查询返回的字段
        if (result.get("filePath") != null) {
            symbol.setFilePath(result.get("filePath").toString());
        }
        if (result.get("line") != null) {
            symbol.setStartLine(((Number) result.get("line")).intValue());
            symbol.setLine(symbol.getStartLine());
        }
        if (result.get("column") != null) {
            symbol.setStartColumn(((Number) result.get("column")).intValue());
            symbol.setColumn(symbol.getStartColumn());
        }
        if (result.get("callerFunction") != null) {
            symbol.setName(result.get("callerFunction").toString());
        }
        
        return symbol;
    }
    
    /**
     * 按模式查询符号
     */
    private List<ASTSymbol> querySymbolsByPattern(String pattern, String projectRoot) {
        // 使用CodeQL查询匹配的符号
        try {
            Map<String, Object> request = new HashMap<>();
            request.put("symbol", pattern);
            request.put("maxResults", 50);
            
            Map<String, Object> queryContext = new HashMap<>();
            queryContext.put("projectRoot", projectRoot);
            request.put("context", queryContext);
            
            @SuppressWarnings("unchecked")
            Map<String, Object> response = dataServiceWebClient.post()
                    .uri("/data/codeql/symbols/ast")
                    .bodyValue(request)
                    .retrieve()
                    .bodyToMono(Map.class)
                    .block();
            
            if (response != null && "200".equals(String.valueOf(response.get("code")))) {
                @SuppressWarnings("unchecked")
                List<Map<String, Object>> symbolsData = (List<Map<String, Object>>) response.get("data");
                List<ASTSymbol> symbols = new ArrayList<>();
                
                if (symbolsData != null) {
                    for (Map<String, Object> symbolData : symbolsData) {
                        ASTSymbol astSymbol = convertToASTSymbol(symbolData);
                        if (astSymbol != null) {
                            symbols.add(astSymbol);
                        }
                    }
                }
                
                return symbols;
            }
        } catch (Exception e) {
            log.error("Failed to query symbols by pattern", e);
        }
        return new ArrayList<>();
    }
}

